#include "fdtable.h"
#include <debug.h>
#include "threads/malloc.h"


void init_file_struct (struct files_struct *fs)
{
//  ASSERT(fs != NULL);
  if (!fs) return;

  lock_init(&fs->file_lock);
  fs->next_fd = 2;
  fs->max_fd = NR_OPEN_DEFAULT;
  fs->fd = &fs->fd_array[0];
}
// here we have both global_fs_lock as well as 
// file_lock on fs.
// close one file and removes it from process fd table

static void __remove_fd(struct files_struct *fs, int fd)
{
  if (fs && fd < fs->next_fd) {
    file_close(fs->fd[fd]);
    fs->fd[fd] = 0;
  }
}

// removes any dynamic fd array talbe allocated, if any
void remove_fd_array(struct files_struct *fs)
{
  if (fs) {
    if (fs->fd != fs->fd_array)
      free(fs->fd);
  }
}

// insert the one FILE into process FILES entry, 
// Dynamically grow to allow files to be added
// ret the FD to caller, which is the entry in process
// fd table.
int insert_filp(struct files_struct *fs, struct file *filp)
{
  int ret = -1, i;
  ASSERT(fs != NULL);

  lock_acquire(&fs->file_lock);

  // keeping max open fd to NR_OPEN_DEFAULT in begining.
  // then grow the fd_table as needed. It has less memory foot-print
  // and array indexed access is faster for frequent file access.

  if (fs->next_fd >= fs->max_fd) {
    struct file **p = (struct file **)calloc((2*fs->max_fd), sizeof(struct file *));
    if (!p) {
      ret = -1;
      goto done;
    }

    fs->max_fd *= 2;

    for (i = 0; i < fs->next_fd - 1; ++i) {
      p[i] = fs->fd[i];
      fs->fd[i] = 0;
    }

    if (fs->fd != fs->fd_array) {
      free(fs->fd);
    }
 //   printf("%s expanded to %d, new array %p, old %p\n", __FUNCTION__, fs->max_fd, p, fs->fd);
    fs->fd = p;
  }
//  ASSERT(fs->next + 1 <= NR_OPEN_DEFAULT);  
  ret = fs->next_fd++;
  fs->fd[ret] = filp;

done:;
  lock_release(&fs->file_lock);
//  printf("%s ret %d\n", __FUNCTION__, ret);
  return ret;
}

void close_all_files (struct files_struct *fs)
{
  if (fs) {
    int i;
	  lock_acquire(&fs->file_lock);
//	printf("ohhhh exit called removing all\n");
    for (i = 2; i < fs->next_fd; ++i)
      __remove_fd(fs, i);
	  lock_release(&fs->file_lock);
  }
}

// assumes global_fs_lock has already been acquired
// SYSCALL_close handler:
// removes fd from process fd table.
void sys_close(struct thread *cur, int fd)
{
  if (cur->files) {
      lock_acquire(&cur->files->file_lock);
      __remove_fd(cur->files, fd);
      lock_release(&cur->files->file_lock);
  }
}
